#include "stdafx.h"
#include "client.h"
#include "../protocol/P2PProtocolImpl.h"
#include "netmgr.h"
#include "../log/logmgr.h"

CClient* g_current_pkg_client = NULL;

CClient::CClient( void )
{

}

CClient::~CClient( void )
{

}

void CClient::Reset( void )
{
    m_session = NULL;
    m_net_handle = NULL;
    m_can_recv = true;
    m_p2p_id = 0;
}

void CClient::OnEstablish( HNETHANDLE hHandle, HSESSION hSession )
{
    m_session = hSession;
    m_net_handle = hHandle;
    m_nat_type = UNKNOW_NAT;

    set_public_ip(CNetMgr::Instance()->get_net_mgr()->GetPeerIP(m_session));
    set_public_port(CNetMgr::Instance()->get_net_mgr()->GetPeerPort(m_session));
}

void CClient::OnTerminate( HSESSION hSession )
{
    CClientMgr::Instance()->DelClient(m_p2p_id);
}

void CClient::OnError( HNETHANDLE hHandle, HSESSION hSession, INT32 iModuleError, INT32 iSystemError )
{

}

void CClient::OnRecv( HSESSION hSession, const char* pData, const INT32 iLen )
{
    if (m_can_recv)
    {
        char* ptr = (char*)pData + sizeof(INT32);
        INT32 size = iLen - sizeof(INT32);

        g_current_pkg_client = this;
        CP2PProtocolImpl::Instance()->HandleClientPkg(ptr, size);
        g_current_pkg_client = NULL;
    }
}

void CClient::Disconnect( void )
{
    if (m_session)
    {
        CNetMgr::Instance()->get_net_mgr()->DisConnect(m_session);
        m_session = NULL;
    }
}

bool CClient::SendPkg( void* host )
{
    char* pkg = NULL;
    int pkg_len = CP2PProtocolImpl::Instance()->BuildPkg(host, pkg);
    if (pkg_len < 0)
    {
        return false;
    }

    if (!m_session)
    {
        return false;
    }

    return CNetMgr::Instance()->get_net_mgr()->Send(m_session, pkg, pkg_len);
}

const char* nat_type_2_str(UINT32 nat_type)
{
    static char nat_type_str[32];

    switch (nat_type)
    {
    case UNKNOW_NAT:
        strcpy(nat_type_str, "Unknow NAT");
        break;
    case FULL_CONE_NAT:
        strcpy(nat_type_str, "Full Cone NAT");
        break;
    case RESTRICTED_CONE_NAT:
        strcpy(nat_type_str, "Restricted Cone NAT");
        break;
    case PORT_RESTRICTED_CONE_NAT:
        strcpy(nat_type_str, "Port Restricted Cone NAT");
        break;
    case SYMMETRIC_NAT:
        strcpy(nat_type_str, "Symmetric NAT");
        break;
    default:
        strcpy(nat_type_str, "Shit NAT");
    }

    return nat_type_str;
}

const char* IP2Str(UINT32 ip)
{
    static char ip_str[32];

    sockaddr_in addr;
    addr.sin_addr.s_addr = ip;
    strcpy(ip_str, inet_ntoa(addr.sin_addr));

    return ip_str;
}

void CClient::set_nat_type( UINT32 nat_type )
{
    if (m_nat_type == nat_type)
    {
        return;
    }

    if (m_nat_type == UNKNOW_NAT)
    {
        NATTestAck stAck;
        stAck.nat_type = (UINT8)m_nat_type;

        SendPkg(&stAck);
    }

    LOG_CRI("********* p2p_id: %u *********", m_p2p_id);
    LOG_CRI("NAT: %s", nat_type_2_str(nat_type));
    LOG_CRI("public  ip= %-16s  port= %-10d", IP2Str(m_public_ip), ntohs(m_public_port));
    LOG_CRI("private ip= %-16s  port= %-10d", IP2Str(m_private_ip), ntohs(m_private_port));
    LOG_CRI("********* end *********\n");
    //LOG_CRI("P2P id= %u Type= %s", m_p2p_id, nat_type_2_str(nat_type));

    m_nat_type = nat_type;
}

CNATTestSession* g_current_nat_test_session = NULL;


void CNATTestSession::Reset( void )
{
    m_session = NULL;
    m_net_handle = NULL;
}

void CNATTestSession::OnEstablish( HNETHANDLE hHandle, HSESSION hSession )
{
    m_session = hSession;
    m_net_handle = hHandle;
}

void CNATTestSession::OnTerminate( HSESSION hSession )
{

}

void CNATTestSession::OnError( HNETHANDLE hHandle, HSESSION hSession, INT32 iModuleError, INT32 iSystemError )
{

}

void CNATTestSession::OnRecv( HSESSION hSession, const char* pData, const INT32 iLen )
{
    char* ptr = (char*)pData + sizeof(INT32);
    INT32 size = iLen - sizeof(INT32);

    g_current_nat_test_session = this;
    CP2PProtocolImpl::Instance()->HandleClientPkg(ptr, size);
    g_current_nat_test_session = NULL;
}

void CNATTestSession::Disconnect( void )
{
    if (m_session)
    {
        CNetMgr::Instance()->get_net_mgr()->DisConnect(m_session);
        m_session = NULL;
    }
}

UINT32 CNATTestSession::local_ip( void )
{
    return CNetMgr::Instance()->get_net_mgr()->GetLocalIP(m_session);
}

UINT16 CNATTestSession::local_port( void )
{
    return CNetMgr::Instance()->get_net_mgr()->GetLocalPort(m_session);
}

UINT32 CNATTestSession::peer_ip( void )
{
    return CNetMgr::Instance()->get_net_mgr()->GetPeerIP(m_session);
}

UINT16 CNATTestSession::peer_port( void )
{
    return CNetMgr::Instance()->get_net_mgr()->GetPeerPort(m_session);
}

CNATTestSession::CNATTestSession( void )
{

}

CNATTestSession::~CNATTestSession( void )
{

}

INSTANCE_SINGLETON(CClientMgr)

bool CClientMgr::Init( void )
{
    m_current_p2p_id = 1000;
    return true;
}

void CClientMgr::UnInit( void )
{

}

UINT32 CClientMgr::AddClient( CClient* client )
{
    if (m_set_clients.find(client) != m_set_clients.end())
    {
        return 0;
    }

    m_current_p2p_id++;

    if (m_current_p2p_id < 1000)
    {
        m_current_p2p_id = 1000;
    }

    INT32 try_count = 0;

    while (m_map_clients.find(m_current_p2p_id) != m_map_clients.end())
    {
        m_current_p2p_id++;
        try_count++;

        if (try_count >= 100000)
        {
            return 0;
        }
    }

    m_map_clients[m_current_p2p_id] = client;
    m_set_clients.insert(client);
    client->set_p2p_id(m_current_p2p_id);

    return m_current_p2p_id;
}

void CClientMgr::DelClient( UINT32 p2p_id )
{
    TClientMapIter it = m_map_clients.find(p2p_id);
    if (it != m_map_clients.end())
    {
        m_set_clients.erase(it->second);
        m_map_clients.erase(it);
    }
}

CClient* CClientMgr::FindClient( UINT32 p2p_id )
{
    TClientMapIter it = m_map_clients.find(p2p_id);
    if (it != m_map_clients.end())
    {
        return it->second;
    }

    return NULL;
}



void CConeTestSession::Reset( void )
{
    m_session = NULL;
    m_p2p_id = 0;
    m_net_handle = NULL;
}

void CConeTestSession::OnEstablish( HNETHANDLE hHandle, HSESSION hSession )
{
    CClient* client = CClientMgr::Instance()->FindClient(m_p2p_id);
    if (client)
    {
        client->set_nat_type(RESTRICTED_CONE_NAT);
    }

    Disconnect();
}

void CConeTestSession::OnTerminate( HSESSION hSession )
{
    CNetMgr::Instance()->get_cone_test_session_pool().FreeObj(this);
}

void CConeTestSession::OnError( HNETHANDLE hHandle, HSESSION hSession, INT32 iModuleError, INT32 iSystemError )
{
    if (iModuleError == NET_CONNECT_FAIL)
    {
        CClient* client = CClientMgr::Instance()->FindClient(m_p2p_id);
        if (client)
        {
            client->set_nat_type(PORT_RESTRICTED_CONE_NAT);
        }
        CNetMgr::Instance()->get_cone_test_session_pool().FreeObj(this);
    }
}

void CConeTestSession::OnRecv( HSESSION hSession, const char* pData, const INT32 iLen )
{

}

void CConeTestSession::Disconnect( void )
{
    if (m_session)
    {
        CNetMgr::Instance()->get_net_mgr()->DisConnect(m_session);
        m_session = NULL;
    }
}

void CP2PUtil::s_2_pr( CClient& src_client, CClient& dst_cient )
{

}

void CP2PUtil::s_2_r( CClient& src_client, CClient& dst_cient )
{

}

void CP2PUtil::s_2_f( CClient& src_client, CClient& dst_cient )
{

}

void CP2PUtil::pr_2_r( CClient& src_client, CClient& dst_cient )
{

}

void CP2PUtil::pr_2_f( CClient& src_client, CClient& dst_cient )
{

}

void CP2PUtil::r_2_f( CClient& src_client, CClient& dst_cient )
{

}

void CP2PUtil::s_2_s( CClient& src_client, CClient& dst_cient )
{

}

void CP2PUtil::r_2_r( CClient& src_client, CClient& dst_cient )
{

}

void CP2PUtil::pr_2_pr( CClient& src_client, CClient& dst_cient )
{

}

void CP2PUtil::f_2_f( CClient& src_client, CClient& dst_cient )
{

}
